# -*- coding = utf-8 -*-
#@Time: 2021/7/17 14:23
#@Author: 卜白
#@File: view.py
#@Software: PyCharm

# 用户APP的视图文件
import re
import apps
import json
from apps import constants
from apps.fictions.models import Book
from apps.users.models import User
from flask import Blueprint, request, jsonify, session, make_response, current_app
from werkzeug.security import generate_password_hash,check_password_hash  # 加密 以及 验证加密的密码

# 图片/短信-验证码
from io import BytesIO
from apps.utils.util import generate_image,SMS_CODE

# url_prefix加链接关键参数，防止两个路由出现重复的\   ,url_prefix='/user'
users = Blueprint('users',__name__)

# 用户注册
@users.route('/register', methods=['GET','POST'])
def register():
    user = User()
    redis_conn = apps.redis_store  # redis链接对象
    if request.method == 'POST':
        # 请求的json数据返回字典
        requ_dict = request.get_json()
        username = requ_dict.get('username').strip()
        password = requ_dict.get('password')
        phone = requ_dict.get('phone')
        email = requ_dict.get('email')
        sms_code = requ_dict.get('smscode')
        # print(username,password,phone,email,img_code)

        # 校验参数
        if not all([username, password, phone, email, sms_code]):
            return jsonify(status=401, msg="参数不完整")

        # 从redis中取出短信验证码
        try:
            real_sms_code = redis_conn.get("sms_code_%s" % phone)
            real_sms_code = str(real_sms_code, encoding="utf-8")  # 将redis获取到的byte类型转化为字符串
        except Exception as e:
            current_app.logger.error(e)
            return jsonify(status=401, msg="读取真实短信验证码异常")

        # 判断短信验证码是否过期
        if real_sms_code is None:
            return jsonify(status=400, msg="短信验证码失效")

        # 删除redis中的短信验证码，防止重复使用校验
        try:
            redis_conn.delete("sms_code_%s" % phone)
        except Exception as e:
            current_app.logger.error(e)

        # 判断用户填写短信验证码的正确性
        if real_sms_code != sms_code:
            return jsonify(status=401, msg="短信验证码错误")

        # 给密码加密generate_password_hash
        password = generate_password_hash(password)
        # 提交注册用户信息 插入数据
        retu_status = user.user_registered(username,password,phone,email)
        if retu_status == 412:
            return jsonify(status=412, msg="用户注册失败！")
        elif retu_status == 200:
            return jsonify(status=200, msg="用户注册成功！")

# 用户登录
@users.route('/login/<types>/<image_code_id>',methods=['GET','POST'])
def login(types,image_code_id):
    user = User()
    redis_conn = apps.redis_store  # redis链接对象
    if request.method == 'POST':
        # 判断是否为密码登录
        if types == 'psd':
            # 请求的json数据返回字典
            requ_dict = request.get_json()
            username = requ_dict.get('username').strip()
            password = requ_dict.get('password')
            img_code = requ_dict.get('img_code')

            try:
                # 从redis中取出生产的图片验证码
                valid = redis_conn.get('img_code_%s' % image_code_id)
                # print('redis数据库中提取的code类型：', type(valid))
                recode = str(valid, encoding="utf-8")
            except Exception as e:
                current_app.logger.error(e)
                return jsonify(status=404, msg="图片验证码已失效！")

            # 判断用户输入的验证码是否一致
            if recode.lower() != img_code.lower():
                return jsonify(status=411, msg="验证码输入错误！")
            
            user_data = user.get_user_all(username)  # 根据用户名查询用户信息
            # 判断用户是否存在
            if user_data:
                # 判断用户输入的密码是否正确
                flag = check_password_hash(user_data['password'],password)
                if flag:
                    user_id = user_data['id']
                    user_name = user_data['username']
                    # session机制，session当成字典使用
                    session['uid'] = user_id   # 将用户id添加到session中
                    # 传入用户id，生成token值
                    token = user.create_token(user_id,user_name)

                    json_data = {
                        'token': token,
                        'status': 200,
                        'msg': '用户登录成功！'
                    }

                    return jsonify(json_data)

                else:
                    return jsonify(status=403, msg="密码输入有误！")

            else:
                return jsonify(status=403, msg="该用户名不存在！")

        # 判断是否为手机号登录
        elif types == 'phone':
            # 请求的json数据返回字典
            requ_dict = request.get_json()
            phone = requ_dict.get('mobile')
            sms_code = requ_dict.get('sms_code')
            try:
                # 从redis中取出短信验证码
                valid = redis_conn.get('sms_code_%s' % phone)
                # print('redis数据库中提取的code类型：', type(valid))
                recode = str(valid, encoding="utf-8")
            except Exception as e:
                current_app.logger.error(e)
                return jsonify(errno=404, errmsg="短信验证码已失效！")

            # 删除redis中的短信验证码，防止重复使用校验
            try:
                redis_conn.delete("sms_code_%s" % phone)
            except Exception as e:
                current_app.logger.error(e)

            # 判断用户输入的短信验证码是否一致
            if recode != sms_code:
                return jsonify(status=411, msg="短信验证码输入错误！")

            # 因为手机号登录发送验证码时，已验证手机号是否存在，则不用再考虑根据手机号查询不到用户信息
            user_data = user.get_user_info(phone)  # 根据手机号查询用户信息
            user_id = user_data['id']
            user_name = user_data['username']
            # session机制，session当成字典使用
            session['uid'] = user_id  # 将用户id添加到session中
            # 传入用户id，生成token值
            token = user.create_token(user_id,user_name)

            json_data = {
                'token': token,
                'status': 200,
                'msg': '用户登录成功！'
            }

            return jsonify(json_data)

# 用户退出
@users.route('/logout', methods=["GET"])
def logout():
    # # 1、cookie的方式
    # response = redirect(url_for('user_bp.index'))
    # # 通过response对象的delete_cookie(key),key就是要删除 的cookie的key
    # response.delete_cookie('uid')
    # # del session['uid']
    # return response

    # 2、session的方式
    # del session['uid']
    session.clear()
    return jsonify(status=200, msg="退出登录！")

# 修改密码  types为判断来源，verif_code为uuid特殊值
@users.route('/changepsd/<types>/<verif_code>', methods=["PUT"])
def changepsd(types,verif_code):
    user = User()
    redis_conn = apps.redis_store  # redis链接对象
    if request.method == 'PUT':
        # 请求的json数据返回字典
        requ_dict = request.get_json()
        # 判断是否为用户名修改密码
        if types == 'uname':
            uname = requ_dict.get('username')
            oldpsd = requ_dict.get('oldpsd')
            newpsd = requ_dict.get('newpsd')
            img_code = requ_dict.get('img_code')
            # print(oldpsd,newold,img_code)

            # 判断新旧密码是否一致
            if oldpsd.strip() == newpsd.strip():
                return jsonify(status=412, msg="新旧密码不可一致！")

            try:
                # 从redis中取出生产的图片验证码
                valid = redis_conn.get('img_code_%s' % verif_code)
                # print('redis数据库中提取的code类型：', type(valid))
                recode = str(valid, encoding="utf-8")
            except Exception as e:
                current_app.logger.error(e)
                return jsonify(errno=404, errmsg="图片验证码已失效！")

            # 判断用户输入的验证码是否一致
            if recode.lower() != img_code.lower():
                return jsonify(status=411, msg="验证码输入错误！")

            user_data = user.get_user_all(uname)  # 根据用户名查询用户信息
            # 判断用户输入的旧密码是否正确
            flag = check_password_hash(user_data['password'], oldpsd)
            if flag:
                # 给新密码加密generate_password_hash
                password = generate_password_hash(newpsd)
                # 数据库更新密码
                retu_status = user.change_password(password,'username',uname)
                if retu_status == 412:
                    return jsonify(status=412, msg="密码修改失败！")
                elif retu_status == 200:
                    return jsonify(status=200, msg="密码修改成功！")

            else:
                return jsonify(status=404, msg="旧密码输入错误！")

        # 判断是否为手机号修改密码
        elif types == 'phone':
            phone = requ_dict.get('mobile')
            newpsd = requ_dict.get('newpsd')
            sms_code = requ_dict.get('sms_code')

            try:
                # 从redis中取出短信验证码
                valid = redis_conn.get('sms_code_%s' % phone)
                # print('redis数据库中提取的code类型：', type(valid))
                recode = str(valid, encoding="utf-8")
            except Exception as e:
                current_app.logger.error(e)
                return jsonify(errno=404, errmsg="短信验证码已失效！")

            # 删除redis中的短信验证码，防止重复使用校验
            try:
                redis_conn.delete("sms_code_%s" % phone)
            except Exception as e:
                current_app.logger.error(e)

            # 判断用户输入的短信验证码是否一致
            if recode != sms_code:
                return jsonify(status=411, msg="短信验证码输入错误！")

            # 验证通过后，给新密码加密generate_password_hash
            password = generate_password_hash(newpsd)
            # 数据库更新密码
            retu_status = user.change_password(password, 'phone', phone)
            if retu_status == 412:
                return jsonify(status=412, msg="密码修改失败！")
            elif retu_status == 200:
                return jsonify(status=200, msg="密码修改成功！")

# 获取短信验证码
@users.route('/get_sms_verification_code/<source>/<re(r"1[34578]\d{9}"):mobile>', methods=['GET'])
def get_sms_verification_code(source,mobile):
    user = User()

    # 判断source来源是属于 登录 还是 注册
    if source == 'log':
        # 若来源属于手机号登录验证，则判断其手机号是否属于未注册
        # 判断手机号是否存在
        try:
            phone = user.get_user_mobile(mobile)  # 查询手机号存在与否
        except Exception as e:
            current_app.logger.error(e)
        else:
            if phone is not None:
                # 表示手机号已存在,发送短信验证码
                SMS_CODE(mobile)
                return jsonify(status=200, msg="短信验证码发送成功！")
            else:
                return jsonify(status=404, msg="该手机号未注册！")

    elif source == 'reg':
        # 若来源属于用户注册验证，则判断其手机号是否属于已注册
        try:
            phone = user.get_user_mobile(mobile)  # 查询手机号存在与否
        except Exception as e:
            current_app.logger.error(e)
        else:
            if phone is None:
                # 表示手机号未注册,发送短信验证码
                SMS_CODE(mobile)
                return jsonify(status=200, msg="短信验证码发送成功！")
            else:
                return jsonify(status=404, msg="该手机号已注册！")

# 获取图片验证码
@users.route('/get_image_verification/<image_code_id>',methods=['GET'])
def get_image_verification(image_code_id):
    redis_conn = apps.redis_store  # redis链接对象
    # im：图片验证码   code：验证码
    im,code = generate_image(4)
    # 将image对象转化为二进制
    buffer = BytesIO()
    im.save(buffer,'JPEG')
    buf_bytes = buffer.getvalue()

    try:    # 异常处理，记录日志
        # 保存验证码到redis中
        # 第一个参数为存储的redis键名，第二个参数为失效时间，第三个参数为真实值
        redis_conn.setex("img_code_%s" % image_code_id, constants.IMAGE_CODE_REDIS_EXPIRES, code)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno="404", errmsg="保存图片验证码失败！")

    response = make_response(buf_bytes)
    response.headers['Content-Type'] = 'image/jpg'
    # # response.headers['Verification-Code'] = code
    # return response
    return buf_bytes

# 解析token，验证登录状态，返回用户信息
@users.route('/resolver/<token>', methods=['GET'])
def resolver(token):
    user = User()

    status = user.verify_auth_token(token)
    # 判断返回的数据属于什么情况
    if status == 405:
        return jsonify(status=405, msg="token值已过期！")
    elif status == 404:
        return jsonify(status=404, msg="token值错误！")
    else:
        uid = status['id']
        uname = status['username']
        json_data = {
            "uid": uid,
            "uname": uname,
            "status": 200,
            "msg": "请求成功"
        }
        return jsonify(json_data)


# 测试路由
@users.route('/test/<username>', methods=['GET'])
def test(username):
    user = User()
    # useres = user.get_user_all(username)
    # print(type(useres),useres['username'])
    # return useres
    # return jsonify(status=200,username=username)

    # token = user.create_token(username)
    # print(type(token))
    token = user.verify_auth_token(username)
    # uid = token.id
    # token = json.loads(token)
    print(type(token),token['id'])
    return token
